#include <iostream>
#include <queue>
#include <unistd.h>
#include <pthread.h>



const int G_capacity = 5;
template<class T>
class BQueue{
public:
  BQueue():_cap(G_capacity)
  {
    pthread_mutex_init(&_mutex, nullptr);
    pthread_cond_init(&_cond, nullptr);
  }

  //生产者任务
  void push(T in)
  {
    //因为要访问阻塞队列 所以第一件事就是加锁
    //然后判断是否满足插入要求
    //满 则 不生产且休眠
    //不满 则 生产 并唤醒消费者
    //再解锁
    _blockWait();
    while(_isFull())
    {
      //阻塞等待
      _blockCondWait();
    }
    _proWork(in);

    _blockRelease();
    _blockCondWakeUpCon();

  }

  //消费者任务
  T pop()
  {
    //先加锁
    //再判断是否满足插入要求
    //空 则 不消费且休眠
    //有 则 消费并唤醒生产者
    //解锁
    _blockWait();
    while(_isEmpty())
    {
      //阻塞等待
      _blockCondWait();
    }
    T ret = _conWork();  
    //std::cout << "assignment success " << std::endl;

    _blockRelease();
    _blockCondWakeUpPro();

    return ret;
  }

  ~BQueue()
  {
    pthread_mutex_destroy(&_mutex);
    pthread_cond_destroy(&_cond);
  }
private:
  void _blockWait()
  {
    pthread_mutex_lock(&_mutex);
  }

  void _blockRelease()
  {
    pthread_mutex_unlock(&_mutex);
  }

  void _blockCondWait()
  {
    pthread_cond_wait(&_cond,&_mutex);
  }

  void _blockCondWakeUpCon()
  {
    pthread_cond_signal(&_cond);
  }

  void _blockCondWakeUpPro()
  {
    pthread_cond_signal(&_cond);
  }
  bool _isFull()
  {
    return _bq.size() ==  _cap;
  }

  bool _isEmpty()
  {
    return _bq.size() == 0;
  }

  void _proWork(T in)
  {
    _bq.push(in);
  }

  T _conWork()
  {
    T temp = _bq.front();
    _bq.pop();
    return temp;
  }
private:
  int _cap;//capacity
  std::queue<T> _bq;
  pthread_mutex_t _mutex;
  pthread_cond_t _cond;
};
